import logging
import socket
import struct
import time
import traceback

from PySide6.QtCore import QObject
from PySide6.QtWidgets import QMessageBox, QWidget

"""
TCP通信自定协议内容:
通信模板:
       数据头起始帧  功能码(模块(2)/功能(2))  后续长度(word)  data0 data1 data2 data3 
    发: FFFF FFFF     00 00               0004           0000  0002  0123  4567
    收: FFFF FFFF     00 00               0004           0000  0002  0123  4567
    
具体举例：
    (1)写数据:
                             起始地址   数据长度      数据
    ----> FFFF FFFF 0101 0004   0000   0002     0123 4567
    ----> FFFF FFFF 0101 0004   0000   0002     0123 4567 
    *正常接收下返回数据与发送数据一致
    
    (2)读数据:
                               起始地址 数据长度   数据 
    ----> FFFF FFFF 0102 0002   0000   0001
    ----> FFFF FFFF 0102 0002   0000   0001     0123  
    * 接收的数据长度与发送的数据长度并不相同
    
    (3)波形开始:
    ----> FFFF FFFF 0201 0000
    ----> FFFF FFFF 0201 0000
    *发送与接收数据相同
    
    (4)波形结束:
    ----> FFFF FFFF 0202 0000
    ----> FFFF FFFF 0202 0000
    *发送与接收数据相同
    
    (5)波形继续:
    ----> FFFF FFFF 0203 0000
    ----> FFFF FFFF 0203 0000
    *发送与接收数据相同
    
    (6)修改波形通道个数
                               通道个数  每包间隔
    ----> FFFF FFFF 0204 0002   0064    0001
    ----> FFFF FFFF 0204 0002   0064    0001
    *发送与接收数据相同
    
    (7)操作e2prom
                              2级操作码  起始    长度
    ----> FFFF FFFF 0301 0003   0001   0000   0001
    ----> FFFF FFFF 0301 0003   0001   0000   0001
    *正常收发下发送与接收数据相同
    *2级操作码:
        1-读取e2prom
        2-保存到e2prom
        3-获取响应结果   长度为 0001 成功 / 长度为 0002 为失败
        4-重置I2C
    
    (8)获取数据下载的结构体信息
    ----> FFFF FFFF 0401 0000   结构体数组个数   结构体1的数据尺寸  结构体2的数据尺寸       
    ----> FFFF FFFF 0401 000B      0005         0000 0001         0000 0002     0000 0003 0000 0004 0000 0005
    
    (9)设置数据下载的起始地址
                            结构体数组的序号
    ----> FFFF FFFF 0402 0001   0001
    ----> FFFF FFFF 0402 0001   0001
    *发送与接收数据相同
    (10)数据下载的写入数据
                                   偏移地址      写入长度      数据
    ----> FFFF FFFF 0403  07D4    0000 0000   0000 07D0  1234 5678 ······  
    写入成功
    ----> FFFF FFFF 0403  0001  FFFF
    写入失败
    ----> FFFF FFFF 0403  0001  0000
    (11)读取或写入加载过缺省值的标志
                                    操作码  0000-请求 0000-修改
    ----> FFFF FFFF 0501  0001  0000/0001
    响应                                    0000-flase 0001-true
    ----> FFFF FFFF 0501  0001  0000/0001
    
        
特殊类型举例:
    (1)第一次连接时密码验证
                               密码高位  密码低位               哈希验证的数据
    ----> FFFF FFFF 00FF 000A   1234    5678   1111 2222 3333 4444 5555 6666 7777 8888 
    二者都成功
    ----> FFFF FFFF 00FF 000A   FFFF    FFFF   1111 2222 3333 4444 5555 6666 7777 8888
    密码验证成功，哈希验证失败
    ----> FFFF FFFF 00FF 000A   0000    FFFF   1111 2222 3333 4444 5555 6666 7777 8888
    密码验证失败
    ----> FFFF FFFF 00FF 000A   0000    0000   1111 2222 3333 4444 5555 6666 7777 8888
     
    (2)远程重启下位机
    ----> FFFF FFFF 00AA 0000
    ----> 无返回值
    
    (3)下位机接收异常
    ----> 任何
    ----> FFFF FFFF 00BB 0000
"""

class Tcp_Connect(QObject):
    """ 自定义通信类 """

    def __init__(self, parent):
        super().__init__()
        # 获取变量
        self.signal_operat = parent.signal_operat
        self.table_info = parent.table_info
        self.queue_logging = parent.queue_logging
        self.config = {
            "port1": "1027",
            "port2": "1028",
            "IP": "192.168.100.11",
            "SN": "255.255.255.0",
            "GW": "192.168.100.1",
            "password": "12341234"
        }

        # 创建变量
        self.pack_str = None  # 解包类型的字符串
        self.hash_list = []   # 认证时需要的哈希值列表
        self.conn_flag = False
        self.create_client()

    def create_client(self):
        """ 创建新 client """
        self.client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.client.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 15000000)  # 扩大buf
        self.client.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 300000000)  # 扩大buf
        self.client.settimeout(1)

    def update_config(self, config):
        """ 更新配置文件 """
        self.config = config

    def connect(self, str_type, hash_list):
        """ 更新解包类型的字符串， 并且连接"""
        self.pack_str = str_type
        self.hash_list = hash_list
        # my_logger.info('连接' + str(self.config['IP']) + str(int(self.config['port1'])))
        self.queue_logging.put([1, 1, '连接' + str(self.config['IP']) + str(int(self.config['port1']))])
        # print(self.config['IP'], int(self.config['port1']))
        try:
            self.client.connect((self.config['IP'], int(self.config['port1'])))
            self.conn_flag = True
            result = self.get_auth(self.config['password'], self.hash_list)
            if result:
                if result == 2:
                    return 'succeed'
                elif result == 1:
                    return 'hash_failed'
            else:
                return 'auth_failed'
        except TimeoutError:
            self.conn_flag = False
            exc_info = traceback.format_exc()
            # my_logger.debug('请求超时\n' + exc_info)
            self.queue_logging.put([1, 0, '请求超时\n' + exc_info])
            # for i in range(3):
            self.create_client()
            try:
                self.client.connect((self.config['IP'], 1029))
                self.create_client()
                return 'bootloader'
            except Exception as e:
                pass
            self.create_client()
            return 'timeout'
        except socket.timeout:
            self.conn_flag = False
            exc_info = traceback.format_exc()
            # my_logger.debug('请求超时\n' + exc_info)
            self.queue_logging.put([1, 0, '请求超时\n' + exc_info])
            # for i in range(3):
            self.create_client()
            try:
                self.client.connect((self.config['IP'], 1029))
                self.create_client()
                return 'bootloader'
            except Exception as e:
                pass
            self.create_client()
            return 'timeout'
        except ConnectionAbortedError:
            self.conn_flag = False
            exc_info = traceback.format_exc()
            # my_logger.warning('连接被断开\n' + exc_info)
            self.queue_logging.put([1, 2, '连接被断开\n' + exc_info])
            self.create_client()
            return '远端服务异常'
        except ConnectionResetError:
            self.conn_flag = False
            exc_info = traceback.format_exc()
            # my_logger.warning('连接被断开\n' + exc_info)
            self.queue_logging.put([1, 2, '连接被断开\n' + exc_info])
            self.create_client()
            return '远端服务异常'
        except OSError:
            self.conn_flag = False
            exc_info = traceback.format_exc()
            # my_logger.warning('连接未成功\n' + exc_info)
            self.queue_logging.put([1, 2, '连接未成功\n' + exc_info])
            self.create_client()
            # return self.connect(self, str_type, hash_list)
            return '远端服务异常'

    def reconnect(self):
        """ 当收到掉线异常时，自行处理连接上线 """
        self.client.shutdown(socket.SHUT_RDWR)
        self.client.close()
        del self.client
        self.create_client()
        result = self.connect(self.pack_str, self.hash_list)
        if result == 'succeed' or result == 'hash_failed':
            pass
        else:
            self.disconnect()
            self.signal_operat.emit(7, 0, [result])

    def disconnect(self):
        """ 断开连接的函数 """
        if self.client:
            self.client.close()
            del self.client
            self.create_client()
            return True
        else:
            return False

    def get_config(self):
        """ 返回配置文件 """
        return self.config

    def send(self, data):
        """ 全局的发送 """
        try:
            if self.conn_flag:
                self.client.send(data)
                # length = self.client.send(data)
                # if length != len(data):
                #     print(len(data), length)
                return True
            else:
                return None
        except TimeoutError:
            self.reconnect()
            exc_info = traceback.format_exc()
            # my_logger.debug('请求超时\n' + exc_info)
            self.queue_logging.put([1, 0, '请求超时\n' + exc_info])
            return None
        except ConnectionAbortedError:
            self.reconnect()
            exc_info = traceback.format_exc()
            # my_logger.debug('连接被断开\n' + exc_info)
            self.queue_logging.put([1, 0, '连接被断开\n' + exc_info])
            return None
        except ConnectionResetError:
            self.reconnect()
            exc_info = traceback.format_exc()
            # my_logger.debug('连接被断开\n' + exc_info)
            self.queue_logging.put([1, 0, '连接被断开\n' + exc_info])
            return None

    def recv(self, length):
        """ 全局的接收 """
        try:
            if self.conn_flag:
                result = b''
                count = 0
                while len(result) < length:
                    result += self.client.recv(1024)
                    # result += self.client.recv(2)
                    # return self.client.recv(length, socket.MSG_WAITALL)
                    if len(result) >= length:
                        return result
                    elif count > 10:
                        return None
            else:
                return None
        except TimeoutError:
            self.reconnect()
            exc_info = traceback.format_exc()
            # my_logger.debug('请求超时\n' + exc_info)
            self.queue_logging.put([1, 0, '请求超时\n' + exc_info])
            return None
        except ConnectionAbortedError:
            self.reconnect()
            exc_info = traceback.format_exc()
            # my_logger.debug('连接被断开\n' + exc_info)
            self.queue_logging.put([1, 0, '连接被断开\n' + exc_info])
            return None
        except ConnectionResetError:
            self.reconnect()
            exc_info = traceback.format_exc()
            # my_logger.debug('连接被断开\n' + exc_info)
            self.queue_logging.put([1, 0, '连接被断开\n' + exc_info])
            return None

    def write_frame(self, head, data):
        """ 打包要发送的数据 """
        frame_head = struct.pack('<6H', *(0xFFFF, 0xFFFF, head[0], 2 + len(data) * 2, head[1], head[2]))
        if data == '':
            frame_data = b''
        else:
            frame_data = self.pack_32bit(data, head[2], head[1])
        return frame_head + frame_data

    def read_frame(self, frame):
        """ 解包收到的数据，并判断长度是否符合帧头数据 """
        frame_head = struct.unpack('<6H', frame[0:12])
        data_len = int((frame_head[3] - 2) / 2)
        if len(frame) == (frame_head[3] + 4) * 2:
            return frame_head, self.unpack_32bit(frame[12:], data_len, frame_head[4])
        else:
            return None

    def pack_32bit(self, frame_data, N, start_addr):
        """ 按照数据的类型 将数据打包成想要的数据 """
        try:
            return struct.pack('<' + self.pack_str[start_addr:start_addr + N], *frame_data)
        except Exception as e:
            exc_info = traceback.format_exc()
            # my_logger.warning('在打包时遇到错误的数据类型\n' + exc_info + '\n addr:' + str(start_addr) + ' ' + str(self.pack_str[start_addr:start_addr + N]))
            self.queue_logging.put([1, 2, '在打包时遇到错误的数据类型\n' + exc_info + '\n addr:' + str(start_addr) + ' ' + str(self.pack_str[start_addr:start_addr + N])])

    def unpack_32bit(self, frame_data, N, start_addr):
        """ 根据数据类型， 将收到的数据拆成想要的数值 """
        return struct.unpack('<' + self.pack_str[start_addr:start_addr + N], frame_data)

    def read_mult_registers(self, start_addr, stop_addr):
        """ 读取数据 从start_addr 到stop_addr"""
        count = stop_addr - start_addr + 1
        data_frame = self.write_frame((0x0102, start_addr, count), '')
        for i in range(3):
            if self.send(data_frame):
                result = self.recv(count * 4 + 12)
                if result:
                    result = self.read_frame(result)
                    if result == None:
                        self.signal_operat.emit(7, 1, ['接收数据时返回长度不匹配'])
                        return None
                    else:
                        if result[0][0] == 0xFFFF and result[0][1] == 0xFFFF and result[0][2] == 0x0102 and result[0][
                            4] == start_addr and result[0][5] == count:
                            return result[1]
                        else:
                            self.signal_operat.emit(7, 1, ['接收数据时返回的帧头不匹配'])
                            return None
                # 没有读到返回值
                else:
                    self.signal_operat.emit(7, 1, ['读取数据接收失败'])
                    pass
            else:
                self.signal_operat.emit(7, 1, ['读取数据发送失败'])
                return None
        return None

    def write_register(self, start_addr, values):
        """ 写数据 """
        data_frame = self.write_frame((0x0101, start_addr, len(values)), values)
        for i in range(3):
            if self.send(data_frame):
                result = self.recv(len(values) * 4 + 12)
                if result:
                    result = self.read_frame(result)
                    if result == None:
                        self.signal_operat.emit(7, 1, ['写入数据时返回长度不匹配'])
                        return None
                    else:
                        if result[0][0] == 0xFFFF and result[0][1] == 0xFFFF and result[0][2] == 0x0101 and result[0][
                            4] == start_addr and result[0][5] == len(values):
                            return True
                        else:
                            self.signal_operat.emit(7, 1, ['写入数据时返回的帧头不匹配'])
                            return None
                else:
                    self.signal_operat.emit(7, 1, ['写入数据接收失败'])
            else:
                self.signal_operat.emit(7, 1, ['写入数据发送失败'])
                return None
        return None

    def read_scope_start(self):
        """ 发送开始波形数据 """
        data_frame = struct.pack('<4H', *(0xFFFF, 0xFFFF, 0x0201, 0x0000))
        if self.send(data_frame):
            result = self.recv(8)
            if result == None:
                self.signal_operat.emit(7, 1, ['发送波形数据开始时接收失败'])
                return None
            else:
                if result == data_frame:
                    return True
                else:
                    self.signal_operat.emit(7, 1, ['发送波形数据开始时返回值异常'])
                    return None
        else:
            self.signal_operat.emit(7, 1, ['发送波形数据开始时发送失败'])
            return None

    def read_scope_stop(self):
        """ 停止波形数据的发送 """
        data_frame = struct.pack('<4H', *(0xFFFF, 0xFFFF, 0x0202, 0x0000))
        if self.send(data_frame):
            result = self.recv(8)
            if result == None:
                self.signal_operat.emit(7, 1, ['发送波形数据停止时接收失败'])
                return None
            else:
                if result == data_frame:
                    return True
                else:
                    self.signal_operat.emit(7, 1, ['发送波形数据停止时返回值异常'])
                    return None
        else:
            self.signal_operat.emit(7, 1, ['发送波形数据停止时发送失败'])
            return None

    def read_scope_continue(self):
        """ 继续发送波形数据 """
        data_frame = struct.pack('<4H', *(0xFFFF, 0xFFFF, 0x0203, 0x0000))
        if self.send(data_frame):
            result = self.recv(8)
            if result == None:
                self.signal_operat.emit(7, 1, ['发送波形数据继续时接收失败'])
                return None
            else:
                if result == data_frame:
                    return True
                else:
                    self.signal_operat.emit(7, 1, ['发送波形数据继续时返回值异常'])
                    return None
        else:
            self.signal_operat.emit(7, 1, ['发送波形数据继续时发送失败'])
            return None

    def send_scope_channel_count(self, count, interval_pack):
        """ 修改波形通道个数 """
        data_frame = struct.pack('<6H', *(0xFFFF, 0xFFFF, 0x0204, 0x0002, count, interval_pack))
        if self.send(data_frame):
            result = self.recv(12)
            if result == None:
                self.signal_operat.emit(7, 1, ['写入通道个数时接收失败'])
                return None
            else:
                if result == data_frame:
                    return True
                else:
                    self.signal_operat.emit(7, 1, ['写入通道个数时返回值异常'])
                    return None
        else:
            self.signal_operat.emit(7, 1, ['写入通道个数时发送失败'])
            return None

    def control_e2prom(self, operation_code, ram_low=0, ram_len=0):
        """ 控制下位机操作EEPROM """
        data_frame = struct.pack('<7H', *(0xFFFF, 0xFFFF, 0x0301, 0x0003, operation_code, ram_low, ram_len))
        if self.send(data_frame):
            result = self.recv(14)
            if result:
                result = struct.unpack('<7H', result)
                if result[0:4] == (0xFFFF, 0xFFFF, 0x0301, 0x0003):
                    if (operation_code == 3) and (result[6] == 2 or result[6]) == 0:
                        self.signal_operat.emit(7, 1, ['e2prom返回值异常'])
                        return None
                    else:
                        return True
                else:
                    self.signal_operat.emit(7, 1, ['e2prom返回值异常'])
                    return None
            else:
                self.signal_operat.emit(7, 1, ['操作e2prom时接收失败'])
                return None
        else:
            self.signal_operat.emit(7, 1, ['操作e2prom时发送失败'])
            return None

    def get_auth(self, auth_pwd, hash_list):
        """ 刚进行TCP连接时需要密码进行验证 """
        pwd_high = int(auth_pwd[0:4], 16)
        pwd_low = int(auth_pwd[4:8], 16)
        data_frame = struct.pack('<14H', *(0xFFFF, 0xFFFF, 0x00FF, 0x000A, pwd_high, pwd_low) + tuple(hash_list))
        if self.send(data_frame):
            result = self.recv(12)
            # print(result)
            if result:
                result = struct.unpack('<6H', result)
                if result[0:4] == (0xFFFF, 0xFFFF, 0x00FF, 0x000A):
                    if result[4:6] == (0xFFFF, 0xFFFF):
                        return 2
                    elif result[4:6] == (0x0000, 0xFFFF):
                        return 1
                    else:
                        self.signal_operat.emit(7, 1, ['密码认证时返回值异常'])
                        return None
                else:
                    self.signal_operat.emit(7, 1, ['密码认证时返回值异常'])
                    return None
            else:
                self.signal_operat.emit(7, 1, ['密码认证时接收失败'])
                return None
        else:
            self.signal_operat.emit(7, 1, ['密码认证时发送失败'])
            return False

    def get_VarandTime(self):
        """  在哈希验证无法通过时获取版本号和时间 """
        data_frame = struct.pack('<7H', *(0xFFFF, 0xFFFF, 0x0302, 0x0003, 0x0001, 501, 2))
        if self.send(data_frame):
            result = self.recv(16)
            if result:
                result = struct.unpack('<4H2L', result)
                if result[0:4] == (0xFFFF, 0xFFFF, 0x0302, 0x0004):
                    return result[4:6]
                else:
                    self.signal_operat.emit(7, 1, ['密码认证时返回值异常'])
                    return None
            else:
                self.signal_operat.emit(7, 1, ['密码认证时接收失败'])
                return None
        else:
            self.signal_operat.emit(7, 1, ['密码认证时发送失败'])
            return False


    def restart_CPU(self):
        """ 远程重启下位机 """
        data_frame = struct.pack('<4H', *(0xFFFF, 0xFFFF, 0x00AA, 0x0000))
        if self.send(data_frame):
            return True
        else:
            self.signal_operat.emit(7, 1, ['重启下位机时发送失败'])
            return False

    def get_writeable_memory(self):
        data = (0xFFFF, 0xFFFF, 0x0401, 0x0000)
        data_frame = struct.pack('<4H', *data)
        if self.send(data_frame):
            result = self.recv(50)
            if result:
                # print(result)
                result = struct.unpack('<5H10L', result)
                # print(result)
                if result[:3] == data[:3]:
                    size_list = []
                    for i in range(result[4]):
                        size_list.append(result[5+i])
                    return size_list
                else:
                    print('数据有问题')
            else:
                print('接收失败')
        else:
            print('发送失败')

    def set_batch_startAddr(self, index):
        data = (0xFFFF, 0xFFFF, 0x0402, 0x0001, index)
        data_frame = struct.pack('<5H', *data)
        if self.send(data_frame):
            result = self.recv(10)
            # print(result)
            if result:
                result = struct.unpack('<5H', result)
                # print(result)
                if result == data:
                    return True
                else:
                    return False
            else:
                return False
        else:
            return False

    def write_batch_data(self, data_tuple):
        # data_length = len(data_tuple) + 6
        data = (0xFFFF, 0xFFFF, 0x0403, len(data_tuple) + 2) + data_tuple
        data_frame = struct.pack('<4H2L{}H'.format(str(len(data_tuple) - 2),), *data)
        # print(data_frame[0:16])
        if self.send(data_frame):
            result = self.recv(10)
            if result:
                result = struct.unpack('<5H', result)
                if result[0:3] == data[0:3] and result[4] == 0xFFFF:
                    return True
                else:
                    # print(data)
                    # print(result)
                    # print(3)
                    return None
            else:
                # print(2)
                return None
        else:
            # print(1)
            return None

    def get_defaultFlag(self, flag):        # flag:  0-请求 1-修改
        data = (0xFFFF, 0xFFFF, 0x0501, 0x0001, flag)
        data_frame = struct.pack('<5H', *data)
        if self.send(data_frame):
            result = self.recv(10)
            # print(result)
            if result:
                result = struct.unpack('<5H', result)
                # print(result)
                if result[0:4] == data[0:4] and result[4] == 1:
                    return True
                elif result[0:4] == data[0:4] and result[4] == 0:
                    return False
                else:
                    return None
            else:
                return None
        else:
            return None